package com.chatplus.application.controller.api;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.chatplus.application.client.pay.WeChatPayClient;
import com.chatplus.application.common.logging.SouthernQuietLogger;
import com.chatplus.application.common.logging.SouthernQuietLoggerFactory;
import com.chatplus.application.constant.PayConstants;
import com.chatplus.application.domain.entity.pay.PayRequestEntity;
import com.chatplus.application.domain.entity.pay.RefundRequestEntity;
import com.chatplus.application.enumeration.PayChannelEnum;
import com.chatplus.application.enumeration.PayStatusEnum;
import com.chatplus.application.service.pay.IPayRequestService;
import com.chatplus.application.service.pay.IRefundRequestService;
import com.chatplus.application.service.pay.PayChannelServiceProvider;
import com.chatplus.application.service.pay.impl.PayChannelService;
import com.chatplus.application.service.pay.impl.PayProcessor;
import com.chatplus.application.service.pay.impl.RefundProcessor;
import com.chatplus.application.web.basecontroller.BaseController;
import com.chatplus.application.web.util.ParameterUtils;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Instant;
import java.util.Map;

/**
 * 小程序支付控制器
 */
@RestController
@RequestMapping("/api/payment")
public class PayNotifyController extends BaseController {

    private static final SouthernQuietLogger LOGGER = SouthernQuietLoggerFactory.getLogger(PayNotifyController.class);

    private final IPayRequestService payRequestService;
    private final PayProcessor payProcessor;
    private final RefundProcessor refundProcessor;
    private final IRefundRequestService refundRequestService;
    private final PayChannelServiceProvider payChannelServiceProvider;
    private final WeChatPayClient weChatPayClient;
    public PayNotifyController(IPayRequestService payRequestService,
                               PayProcessor payProcessor,
                               RefundProcessor refundProcessor,
                               PayChannelServiceProvider payChannelServiceProvider,
                               IRefundRequestService refundRequestService,
                               WeChatPayClient weChatPayClient) {
        this.payRequestService = payRequestService;
        this.payProcessor = payProcessor;
        this.refundProcessor = refundProcessor;
        this.refundRequestService = refundRequestService;
        this.payChannelServiceProvider = payChannelServiceProvider;
        this.weChatPayClient = weChatPayClient;
    }

    public static final String PARAMS = "params";

    /**
     * 支付宝支付结果回调通知
     * https://opendocs.alipay.com/open/270/105902?ref=api#%E4%BA%A4%E6%98%93%E7%8A%B6%E6%80%81%E8%AF%B4%E6%98%8E
     */
    @Tag(name = "alipayNotify", description = "支付宝结果回调通知")
    @RequestMapping("/alipay/notify")
    @SaIgnore
    public String alipayNotify(HttpServletRequest request) {
        Map<String, String> params = ParameterUtils.toMap(request);
        try {
            LOGGER.message("支付宝回调原始参数：").context(PARAMS, params).info();
            // 获取支付宝POST过来反馈信息校验
            PayChannelService payChannelService = payChannelServiceProvider.getPayChannelService(PayChannelEnum.ALIPAY);
            //1、验签
            boolean verifyResult = payChannelService.verifySign(params);
            if (!verifyResult) {
                LOGGER.message("支付宝回调验证失败").context("result", params).context("request", request.getParameterMap()).error();
                return PayConstants.Alipay.NOTIFY_FAILURE;
            }
            //2、查询支付请求
            // 商家订单号。原支付请求的商家订单号
            String payTransactionId = params.get("out_trade_no");
            PayRequestEntity payRequestEntity = payRequestService.findByPayTransactionId(payTransactionId);
            if (payRequestEntity == null) {
                LOGGER.message("查找不到对应的支付记录").context("result", params).context("request", request.getParameterMap()).error();
                return PayConstants.Alipay.NOTIFY_FAILURE;
            }
            // refundTransactionId 如果存在则是退款的
            // 退款申请流水号
            String refundTransactionId = params.get("out_biz_no");
            String orderStatus = params.get("trade_status");

            if (CharSequenceUtil.isEmpty(refundTransactionId)) {
                if (PayConstants.Alipay.TRADE_SUCCESS.equals(orderStatus)) {
                    //3、修改支付结果
                    DateTime paySuccessTime = DateUtil.parse(params.get("gmt_payment"));
                    Instant successAt = paySuccessTime == null ? Instant.now() : paySuccessTime.toInstant();
                    String payerInfo = StringUtils.trimToEmpty(params.get("buyer_id"));
                    // 外部订单号
                    String tradeTransactionId = params.get("trade_no");
                    // 支付宝需要再去查询一次才能知道支付者的信息
                    payRequestEntity.setTradeTransactionId(tradeTransactionId);
                    payRequestEntity.setSuccessAt(successAt);
                    payRequestEntity.setPayerInfo(payerInfo);
                    payProcessor.onPaySuccess(payRequestEntity);
                    LOGGER.message("支付宝支付回调成功处理数据成功").context(PARAMS, params).debug();
                    return PayConstants.Alipay.NOTIFY_SUCCESS;
                }
            } else {
                // 支付宝退款包含 退款的异步通知参数中一定会返回
                // out_biz_no（商户订单号）、refund_fee（本次退款金额）、gmt_refund（退款时间）
                // 先不处理退款回调逻辑
                return PayConstants.Alipay.NOTIFY_SUCCESS;
            }
        } catch (Exception e) {
            LOGGER.message("支付宝回调处理出错").context("request", request.getParameterMap()).exception(e).error();
        }
        return PayConstants.Alipay.NOTIFY_FAILURE;
    }


    /**
     * 支付回调通知处理
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_5.shtml
     */
    @PostMapping("/wechat/notify")
    @SaIgnore
    public String wechatNotify(@RequestBody String data, HttpServletRequest request) throws WxPayException {
        // 我们提供给微信的流水号
        JSONObject dataJson = JSONUtil.parseObj(data);
        // 通知类型
        String eventType = dataJson.getStr("event_type");
        // 支付回调
        if (eventType.startsWith(PayConstants.Wechat.EVENT_TYPE_TRANSACTION)) {
            return payNotifyHandle(data, request);
        }
        // 退款回调
        else if (eventType.startsWith(PayConstants.Wechat.EVENT_TYPE_REFUND)) {
            return refundNotifyHandle(data, request);
        }
        return WxPayNotifyResponse.fail("未知");
    }



    private String payNotifyHandle(String data, HttpServletRequest request) throws WxPayException {
        WxPayNotifyV3Result v3Result = weChatPayClient.parseOrderNotifyV3Result(data, request);
        LOGGER.message("微信支付回调参数：").context("params", v3Result).info();
        String payTransactionId = v3Result.getResult().getOutTradeNo();
        String payStatus = v3Result.getResult().getTradeState();
        String tradeStateDesc = v3Result.getResult().getTradeStateDesc();
        PayRequestEntity payRequestEntity = payRequestService.findByPayTransactionId(payTransactionId);
        //2、查询支付请求
        // 商家订单号。原支付请求的商家订单号
        if (payRequestEntity == null) {
            LOGGER.message("查找不到对应的支付记录").context("result", v3Result).error();
            return WxPayNotifyResponse.fail("失败");
        }
        if (payRequestEntity.getPayStatus() != PayStatusEnum.NOT) {
            // 订单可能在其他渠道已经成功了
            return WxPayNotifyResponse.success("处理成功");
        }
        switch (payStatus) {
            case PayConstants.Wechat.TRADE_SUCCESS:
                //3、修改支付结果
                String successTime = v3Result.getResult().getSuccessTime();
                DateTime paySuccessTime = DateUtil.parse(successTime);
                String payerInfo = StringUtils.trimToEmpty(v3Result.getResult().getPayer().getOpenid());
                // 外部订单号
                String tradeTransactionId = v3Result.getResult().getTransactionId();
                payRequestEntity.setPayTransactionId(Long.valueOf(payTransactionId));
                payRequestEntity.setSuccessAt(paySuccessTime.toInstant());
                payRequestEntity.setPayerInfo(payerInfo);
                payRequestEntity.setTradeTransactionId(tradeTransactionId);
                payRequestEntity.setErrCode(payStatus);
                payRequestEntity.setErrCodeDes(tradeStateDesc);
                payProcessor.onPaySuccess(payRequestEntity);
                return WxPayNotifyResponse.success("支付成功回调处理成功");
            case PayConstants.Wechat.TRADE_CLOSED:
                payRequestEntity.setErrCode(payStatus);
                payRequestEntity.setErrCodeDes(tradeStateDesc);
                payProcessor.onPayFail(payRequestEntity);
                return WxPayNotifyResponse.success("支付失败回调处理成功");
            default:
                return WxPayNotifyResponse.success("未知状态支付回调成功");
        }
    }

    private String refundNotifyHandle(String data, HttpServletRequest request) throws WxPayException {
        WxPayRefundNotifyV3Result v3Result = weChatPayClient.parseRefundNotifyV3Result(data, request);
        LOGGER.message("微信退款回调参数：").context("params", v3Result).info();
        // 商户退款单号
        String refundId = v3Result.getResult().getOutRefundNo();
        RefundRequestEntity aspRefundRequestEntity = refundRequestService.findByRefundApplySeqNo(refundId);
        if (aspRefundRequestEntity == null) {
            LOGGER.message("查找不到对应的退款记录").context("result", v3Result).error();
            return WxPayNotifyResponse.success("查找不到对应的退款记录");
        }
        refundProcessor.queryRefund(aspRefundRequestEntity);
        return WxPayNotifyResponse.success("处理成功");
    }
}
